<!DOCTYPE html>
<html>

<head>
    <title>video recoder</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
</head>
<style>
    body {
        background-color: #EFEDEF;
    }
</style>

<body>
    <article style="border:1px solid white;width:400px;height:400px;margin:0 auto;background-color:white;">

        <section class="experiment" style="width:320px; height:240px;border:1px solid green; margin:50px auto;">
            <div id="videos-container" style="width:320px; height:240px;">
            </div>
        </section>
        <section class="experiment" style="text-align:center;border:none; margin-top:20px;">
            <button id="openCamera">打开摄像头</button>
            <button id="start-recording" disabled>开始录制</button>
            <button id="save-recording" disabled>保存</button>
            <!--<a href="javascript:void(0)" onclick="send()">发送</a>-->
        </section>
    </article>
    <script>
        var mediaStream;
        var recorderFile;
        var stopRecordCallback;
        var openBtn = document.getElementById("openCamera");
        var startBtn = document.getElementById("start-recording");
        var saveBtn = document.getElementById("save-recording");
        openBtn.onclick = function() {
            this.disabled = true;
            startBtn.disabled = false;
            openCamera();
        };

        startBtn.onclick = function() {
            this.disabled = true;
            startRecord();
        };

        saveBtn.onclick = function() {
            saver();

            // alert('Drop WebM file on Chrome or Firefox. Both can play entire file. VLC player or other players may not work.');
        };

        var mediaRecorder;
        var videosContainer = document.getElementById('videos-container');

        function openCamera() {
            var len = videosContainer.childNodes.length;
            for (var i = 0; i < len; i++) {
                videosContainer.removeChild(videosContainer.childNodes[i]);
            }

            var video = document.createElement('video');

            var videoWidth = 320;
            var videoHeight = 240;

            video.controls = true;
            video.muted = true;
            video.width = videoWidth;
            video.height = videoHeight;
            MediaUtils.getUserMedia(true, false, function(err, stream) {
                if (err) {
                    throw err;
                } else {
                    // 通过 MediaRecorder 记录获取到的媒体流
                    console.log();
                    mediaRecorder = new MediaRecorder(stream);
                    mediaStream = stream;
                    var chunks = [],
                        startTime = 0;
                    video.srcObject = stream;
                    video.play();

                    videosContainer.appendChild(video);
                    mediaRecorder.ondataavailable = function(e) {
                        mediaRecorder.blobs.push(e.data);
                        chunks.push(e.data);
                    };
                    mediaRecorder.blobs = [];

                    mediaRecorder.onstop = function(e) {
                        recorderFile = new Blob(chunks, {
                            'type': mediaRecorder.mimeType
                        });
                        chunks = [];
                        if (null != stopRecordCallback) {
                            stopRecordCallback();
                        }
                    };
                }
            });
        }

        // 停止录制
        function stopRecord(callback) {
            stopRecordCallback = callback;
            // 终止录制器
            mediaRecorder.stop();
            // 关闭媒体流
            MediaUtils.closeStream(mediaStream);
        }

        var MediaUtils = {
            /**
             * 获取用户媒体设备(处理兼容的问题)
             * @param videoEnable {boolean} - 是否启用摄像头
             * @param audioEnable {boolean} - 是否启用麦克风
             * @param callback {Function} - 处理回调
             */
            getUserMedia: function(videoEnable, audioEnable, callback) {
                navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
                    navigator.msGetUserMedia || window.getUserMedia;
                var constraints = {
                    video: videoEnable,
                    audio: audioEnable
                };
                if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
                    navigator.mediaDevices.getUserMedia(constraints)
                        .then(function(stream) {
                            callback(false, stream);
                        }).catch(function(err) {
                            callback(err);
                        });
                } else if (navigator.getUserMedia) {
                    navigator.getUserMedia(constraints, function(stream) {
                        callback(false, stream);
                    }, function(err) {
                        callback(err);
                    });
                } else {
                    callback(new Error('Not support userMedia'));
                }
            },

            /**
             * 关闭媒体流
             * @param stream {MediaStream} - 需要关闭的流
             */
            closeStream: function(stream) {
                if (typeof stream.stop === 'function') {
                    stream.stop();
                } else {
                    let trackList = [stream.getAudioTracks(), stream.getVideoTracks()];

                    for (let i = 0; i < trackList.length; i++) {
                        let tracks = trackList[i];
                        if (tracks && tracks.length > 0) {
                            for (let j = 0; j < tracks.length; j++) {
                                let track = tracks[j];
                                if (typeof track.stop === 'function') {
                                    track.stop();
                                }
                            }
                        }
                    }
                }
            }
        };

        function startRecord() {
            mediaRecorder.start();
            setTimeout(function() {
                // 结束
                stopRecord(function() {
                    alert("录制成功!");
                    openBtn.disabled = false;
                    saveBtn.disabled = false;
                    //send();
                });
            }, 20000);
        }

        function saver() {
            var file = new File([recorderFile], 'msr-' + (new Date).toISOString().replace(/:|\./g, '-') + '.mp4', {
                type: 'video/mp4'
            });
            saveAs(file);
        }

        function send() {
            var file = new File([recorderFile], 'msr-' + (new Date).toISOString().replace(/:|\./g, '-') + '.mp4', {
                type: 'video/mp4'
            });
            var data = new FormData();
            data.append("username", "test");
            data.append("userfile", file);

            var req = new XMLHttpRequest();
            req.open("POST", "com.spinsoft.bip.frame.utils.image.saveMp4.biz.ext");
            req.send(data);
        }
    </script>

</body>

</html>

<script>
    /* FileSaver.js
     * A saveAs() FileSaver implementation.
     * 1.3.2
     * 2016-06-16 18:25:19
     *
     * By Eli Grey, http://eligrey.com
     * License: MIT
     *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
     */

    /*global self */
    /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

    /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */

    var saveAs = saveAs || (function(view) {
        "use strict";
        // IE <10 is explicitly unsupported
        if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
            return;
        }
        var
            doc = view.document
            // only get URL when necessary in case Blob.js hasn't overridden it yet
            ,
            get_URL = function() {
                return view.URL || view.webkitURL || view;
            },
            save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"),
            can_use_save_link = "download" in save_link,
            click = function(node) {
                var event = new MouseEvent("click");
                node.dispatchEvent(event);
            },
            is_safari = /constructor/i.test(view.HTMLElement) || view.safari,
            is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent),
            throw_outside = function(ex) {
                (view.setImmediate || view.setTimeout)(function() {
                    throw ex;
                }, 0);
            },
            force_saveable_type = "application/octet-stream"
            // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
            ,
            arbitrary_revoke_timeout = 1000 * 40 // in ms
            ,
            revoke = function(file) {
                var revoker = function() {
                    if (typeof file === "string") { // file is an object URL
                        get_URL().revokeObjectURL(file);
                    } else { // file is a File
                        file.remove();
                    }
                };
                setTimeout(revoker, arbitrary_revoke_timeout);
            },
            dispatch = function(filesaver, event_types, event) {
                event_types = [].concat(event_types);
                var i = event_types.length;
                while (i--) {
                    var listener = filesaver["on" + event_types[i]];
                    if (typeof listener === "function") {
                        try {
                            listener.call(filesaver, event || filesaver);
                        } catch (ex) {
                            throw_outside(ex);
                        }
                    }
                }
            },
            auto_bom = function(blob) {
                // prepend BOM for UTF-8 XML and text/* types (including HTML)
                // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
                if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
                    return new Blob([String.fromCharCode(0xFEFF), blob], {
                        type: blob.type
                    });
                }
                return blob;
            },
            FileSaver = function(blob, name, no_auto_bom) {
                if (!no_auto_bom) {
                    blob = auto_bom(blob);
                }
                // First try a.download, then web filesystem, then object URLs
                var
                    filesaver = this,
                    type = blob.type,
                    force = type === force_saveable_type,
                    object_url, dispatch_all = function() {
                        dispatch(filesaver, "writestart progress write writeend".split(" "));
                    }
                    // on any filesys errors revert to saving with object URLs
                    ,
                    fs_error = function() {
                        if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
                            // Safari doesn't allow downloading of blob urls
                            var reader = new FileReader();
                            reader.onloadend = function() {
                                var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
                                var popup = view.open(url, '_blank');
                                if (!popup) view.location.href = url;
                                url = undefined; // release reference before dispatching
                                filesaver.readyState = filesaver.DONE;
                                dispatch_all();
                            };
                            reader.readAsDataURL(blob);
                            filesaver.readyState = filesaver.INIT;
                            return;
                        }
                        // don't create more object URLs than needed
                        if (!object_url) {
                            object_url = get_URL().createObjectURL(blob);
                        }
                        if (force) {
                            view.location.href = object_url;
                        } else {
                            var opened = view.open(object_url, "_blank");
                            if (!opened) {
                                // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
                                view.location.href = object_url;
                            }
                        }
                        filesaver.readyState = filesaver.DONE;
                        dispatch_all();
                        revoke(object_url);
                    };
                filesaver.readyState = filesaver.INIT;

                if (can_use_save_link) {
                    object_url = get_URL().createObjectURL(blob);
                    setTimeout(function() {
                        save_link.href = object_url;
                        save_link.download = name;
                        click(save_link);
                        dispatch_all();
                        revoke(object_url);
                        filesaver.readyState = filesaver.DONE;
                    });
                    return;
                }

                fs_error();
            },
            FS_proto = FileSaver.prototype,
            saveAs = function(blob, name, no_auto_bom) {
                return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
            };
        // IE 10+ (native saveAs)
        if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
            return function(blob, name, no_auto_bom) {
                name = name || blob.name || "download";

                if (!no_auto_bom) {
                    blob = auto_bom(blob);
                }
                return navigator.msSaveOrOpenBlob(blob, name);
            };
        }

        FS_proto.abort = function() {};
        FS_proto.readyState = FS_proto.INIT = 0;
        FS_proto.WRITING = 1;
        FS_proto.DONE = 2;

        FS_proto.error =
            FS_proto.onwritestart =
            FS_proto.onprogress =
            FS_proto.onwrite =
            FS_proto.onabort =
            FS_proto.onerror =
            FS_proto.onwriteend =
            null;

        return saveAs;
    }(
        typeof self !== "undefined" && self ||
        typeof window !== "undefined" && window ||
        this.content
    ));
    // `self` is undefined in Firefox for Android content script context
    // while `this` is nsIContentFrameMessageManager
    // with an attribute `content` that corresponds to the window

    if (typeof module !== "undefined" && module.exports) {
        module.exports.saveAs = saveAs;
    } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
        define("FileSaver.js", function() {
            return saveAs;
        });
    }
</script>